package run.siyuan.service;

import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.repository.DeploymentBuilder;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import run.siyuan.factory.FlowServiceFactory;
import run.siyuan.mapper.HisApprovalMapper;
import run.siyuan.mapper.QjMapper;
import run.siyuan.model.FlowReq;
import run.siyuan.model.HisApproval;
import run.siyuan.model.ProcessDefinitionVo;
import run.siyuan.model.TaskVo;

import java.io.*;
import java.util.Date;
import java.util.Map;
import java.util.zip.ZipInputStream;

/**
 * @ClassName FlowServiceImpl
 * @Description TODO
 * @Author siyuan
 * @Date 2023/4/15 19:19
 */
@Slf4j
@Service
public class FlowService extends FlowServiceFactory {

    @Autowired
    private QjMapper qjMapper;
    @Autowired
    private HisApprovalMapper hisApprovalMapper;

    public void deploy(String name, MultipartFile file) {
        try {
            DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();
            deploymentBuilder.name(name);
            String fileName = file.getOriginalFilename();
            if (fileName.endsWith(".bpmn20.xml") || fileName.endsWith(".bpmn")) {
                deploymentBuilder.addInputStream(file.getOriginalFilename(), file.getInputStream());
            } else if (fileName.endsWith(".bar") || fileName.endsWith(".zip")) {
                deploymentBuilder.addZipInputStream(new ZipInputStream(file.getInputStream()));
            } else {
                throw new RuntimeException("文件类型不是 .bpmn20.xml 或者 .bpmn 或者 .bar 或者.zip");
            }
            //部署成功后 act_re_deployment，act_re_procdef，act_ge_bytearray 表就有数据了
            deploymentBuilder.deploy();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public void deploy(String name, File file) {
        try {
            DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();
            deploymentBuilder.name(name);
            InputStream inputStream = new FileInputStream(file);
            String fileName = file.getName();

            if (fileName.endsWith(".bpmn20.xml") || fileName.endsWith(".bpmn")) {
                deploymentBuilder.addInputStream(fileName, inputStream);
            } else if (fileName.endsWith(".bar") || fileName.endsWith(".zip")) {
                deploymentBuilder.addZipInputStream(new ZipInputStream(inputStream));
            } else {
                throw new RuntimeException("文件类型不是 .bpmn20.xml 或者 .bpmn 或者 .bar 或者.zip");
            }
            //部署成功后 act_re_deployment，act_re_procdef，act_ge_bytearray 表就有数据了
            deploymentBuilder.deploy();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 以xml 部署
     *
     * @param name
     * @param xml
     */
    public void deploy(String name, String xml) {
        DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();
        deploymentBuilder.name(name)
                .addInputStream(name + ".bpmn", new ByteArrayInputStream(xml.getBytes()))
                .deploy();
    }

    /**
     * 分页查询流程定义信息
     *
     * @param current 当前页
     * @param size    每页条数
     */
    public IPage<ProcessDefinitionVo> getProcessDefinitionPage(int current, int size) {
        Page<ProcessDefinitionVo> page = new Page<>(current, size);
        IPage<ProcessDefinitionVo> processDefinitionVoIPage = qjMapper.getProcessDefinitionPage(page);
        return processDefinitionVoIPage;
    }

    /**
     * 挂载/激活 流程定义
     *
     * @param req
     */
    public void mountActivation(FlowReq req) {
        // 获取流程定义
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(req.getProcessDefinitionId()).singleResult();
        if (processDefinition.isSuspended()) {
            //如果流程是悬挂 那么激活他
            repositoryService.activateProcessDefinitionById(req.getProcessDefinitionId());
        } else {
            //如果是正常的 那么挂起他
            repositoryService.suspendProcessDefinitionById(req.getProcessDefinitionId());
        }
    }

    /**
     * 启动一个流程
     *
     * @param processDefinitionKey
     */
    public String startProcessInstance(String processDefinitionKey) {
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey);
        return processInstance.getId();
    }

    /**
     * 流程定义key+业务表主键+申请单名称
     *
     * @param instanceId 流程实例id 如果不为空表示还没有走完这个流程 不需要发起
     */
    public String startProcessInstance(String businessKey, Map<String, Object> map,
                                       String instanceId) {
        //截取业务key
        String[] res = businessKey.split("\\.");
        //流程定义key
        String processDefinitionKey = res[0];
        //业务表主键
        String businessId = res[1];
        //我这里写死 你要改成 申请单传入过来的账号
        String userId = "zhangsan";

        if (StrUtil.isEmpty(instanceId)) {
            ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey, map);
            if (processInstance == null) {
                throw new RuntimeException("启动流程实例失败");
            }
            instanceId = processInstance.getId();
        }
        //启动流程实例之后 完成第一个任务 就是提交申请单这一个节点  这样才能走到领导审批的节点
        //根据流程实例id 获取任务
        Task task = taskService.createTaskQuery()
                .processInstanceId(instanceId)
                .singleResult();
        //先认领任务
        taskService.claim(task.getId(), userId);
        //在完成任务
        taskService.complete(task.getId(), map);
        //添加历史审批
        insertHisApproval("", userId, businessId, task.getName());
        //返回流程实例id
        return instanceId;
    }


    /**
     * 添加历史审批
     *
     * @param approvalComments 审批意见
     * @param assignee         审批人
     * @param businessId       业务表主键
     * @param taskName         节点名称
     */
    public void insertHisApproval(String approvalComments, String assignee, String businessId, String taskName) {
        HisApproval hisApproval = new HisApproval();
        hisApproval.setId(UUID.randomUUID().toString().replaceAll("-", ""));
        hisApproval.setApprovalComments(approvalComments);
        hisApproval.setApprovalName(assignee);
        hisApproval.setBusinessId(businessId);
        //任务节点
        hisApproval.setTaskName(taskName);
        hisApproval.setApprovalTime(new Date());
        hisApprovalMapper.insert(hisApproval);
    }

    /**
     * 认领 审批单
     *
     * @param taskId
     * @param account
     */
    public void claim(String taskId, String account) {
        //吧act_ru_task表的ASSIGNEE_ 设置为account 来审批
        taskService.claim(taskId, account);
    }

    /**
     * 审批
     *
     * @param businessId       业务表主键
     * @param map              分支条件
     * @param approvalComments 审批意见
     * @param instanceId       流程实例id
     * @param assignee         审批人
     */
    public String complete(String instanceId, Map<String, Object> map,
                           String businessId, String approvalComments,
                           String assignee) {
        //根据流程实例id 获取任务
        Task task = taskService.createTaskQuery()
                .processInstanceId(instanceId)
                .taskAssignee(assignee)
                .singleResult();
        if (task == null) {
            throw new RuntimeException("任务不存在,或者不是当前任务的审批人");
        }
        //完成任务
        taskService.complete(task.getId(), map);
        //审批完成后 再次查询任务是否还有数据
        long count = taskService.createTaskQuery()
                .processInstanceId(instanceId)
                .count();
        //添加历史审批
        insertHisApproval(approvalComments, assignee, businessId, task.getName());
        if (count == 0) {
            //返回结束 修改表单的状态
            return "end";
        }
        return "ok";
    }


}
